package org.sean.framework.canary.core;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.sean.framework.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.builder.ParentContextApplicationContextInitializer;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.endpoint.event.RefreshEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 灰度规则
 *
 * @author xielei
 */
@Slf4j
@Data
@NoArgsConstructor
@RefreshScope
@ConfigurationProperties(prefix = "canary")
public class CanaryProperties implements ApplicationListener<ApplicationEvent> {

    public static final String METADATA_LABEL = "label";
    /**
     * 规则列表
     */
    private List<Canary> rules = new ArrayList<>();

    /**
     * 默认规则
     */
    private Default main;

    /**
     * 排除指定ip段服务
     */
    private Set<String> blackIps = new HashSet<>();

    private Set<String> whiteIps = new HashSet<>();

    private Set<Class<?>> events = new HashSet<>();

    private String ip;
    private String serverName;
    private String instanceId;

    @Autowired
    private InetUtils inetUtils;
    @Autowired(required = false)
    private ServiceInstance serviceInstance;

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof RefreshEvent || event instanceof ContextRefreshedEvent) {
            refreshEvent();
        } else if (event instanceof ParentContextApplicationContextInitializer.ParentContextAvailableEvent
                || event instanceof ApplicationReadyEvent) {
            normalEvent(event);
        }
    }

    private void normalEvent(ApplicationEvent event) {
        if (!events.contains(event.getClass())) {
            CanaryUtils.initRules(this);
            events.add(event.getClass());
        }
    }

    private void refreshEvent() {
        CanaryUtils.initRules(this);
    }

    /**
     * 当前服务IP
     *
     * @return ip
     */
    public String getLocalIp() {
        try {
            if (StringUtil.isBlank(ip)) {
                ip = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
            }
        } catch (Exception e) {
            // 初始化时调用 NullPointException
        }
        return ip;
    }

    /**
     * 当前服务 Service Id
     *
     * @return serviceId
     */
    public String getLocalServiceId() {
        try {
            if (StringUtil.isBlank(serverName)) {
                serverName = serviceInstance.getServiceId();
            }
        } catch (Exception e) {
            // 初始化时调用 NullPointException
        }
        return serverName;
    }

    /**
     * 当前服务实例ID
     *
     * @return 实例ID
     */
    public String getLocalInstanceId() {
        try {
            if (StringUtil.isBlank(instanceId)) {
                instanceId = serviceInstance.getInstanceId();
            }
        } catch (Exception e) {
            // 初始化时调用 NullPointException
        }
        return instanceId;
    }

    /**
     * 当前服务Label
     *
     * @return label
     */
    public String getLocalLabel() {
        try {
            return serviceInstance.getMetadata().getOrDefault(METADATA_LABEL, "");
        } catch (Exception e) {
            // 初始化时调用 NullPointException
        }
        return "";
    }

    /**
     * <pre>
     * 灰度配置项
     * 普通配置:
     * clientId -&gt; label
     * deviceId -&gt; label
     * targetIp -&gt; label
     * targetId -&gt; label
     * </pre>
     */
    @Data
    @NoArgsConstructor
    public static class Canary {
        /**
         * clientId
         */
        private String clientId;
        /**
         * deviceId
         */
        private String deviceId;
        /**
         * targetIp
         */
        private String targetIp;
        /**
         * targetId
         */
        private String targetId;
        /**
         * weight
         */
        private String weight;
        /**
         * label
         */
        private Set<String> labels;
    }

    /**
     * 默认Label
     */
    @Data
    @NoArgsConstructor
    public static class Default {
        private String label;
        private Set<Server> servers;
    }

    /**
     * 默认服务
     */
    @Data
    @NoArgsConstructor
    @EqualsAndHashCode
    public static class Server {
        private String name;
        private String label;
    }
}
